'''
class 类名:
    属性\方法
属性：变量（）
方法：函数（）
    

'''
"""

#定义一个人类
class Person:
    name='小明'   #类属性
    def work():#类方法
        print(f'{Person.name}的工作是厨师')
#这里的name 是类属性我们需要通过类名进行调用
#变量是属性，函数是方法

#上面我们就实现了一个类的创建了
#类是通过对象进行实现后续的操作的
#那么我们就需要创建一个对象

#创建对象：类名（）
print(Person)
#<class '__main__.Person'>

p1=Person()
print(p1)
#<__main__.Person object at 0x0196F970>
#这个p就是我们创建的对象
#我们每次创建对象编译器就会为其进行空间的分配
#一个类是可以进行多个对象的创建的
p2=Person()
#我们对于类属性和类方法的访问
#通过对象直接对类中的元素进行访问
print(p1.name)


#p1.work()
#这么写是会报错的
#对象在调用方法时，会将对象信息传递给方法
#所以我们在利用对象调用类方法的时候是传递了一个类似于指针的东西
#但是我们的函数中并没有进行接收的操作
#所以这个是会进行报错的

#所以我们在调用类方法的时候我们是不能直接通过对象进行调用的


#综上所述：

#想要调用类属性的话
#类名.属性名
print(Person.name)
#想要调用类方法的话
#类名.方法名
print(Person.work())


#小明
#小明
#小明的工作是厨师

"""
"""
class Person:
    name='小明'   #类属性
    def work():#类方法
        print(f'{Person.name}的工作是厨师')
    def job(self):#实例方法
        print(f'self:{self}')
    

p1=Person()
p1.job()
#<__main__.Person object at 0x03C29AB0> 这个就是对象的地址了

#我们只要将类中的方法实例化我们就能通过对象进行类方法的调用了

#我们在类方法中定义了self用来接受传过来的对象信息
"""

"""
class Mode:#模型类
    #类方法：对于所有实例对象统一的功能
    def mode():
        print("这是一个苹果15手机壳模型")
    #实例方法：应用于对象，对于对象不同，内容不同
    def types(self0,color,mat):
        print(f"这是一个{color}颜色{mat}材质的手机壳")

#调用类方法
Mode.mode()#类名.类方法进行调用
#这是一个苹果15手机壳模型

#调用示例方法：实例对象.实例方法（）
#创建实例对象
m1=Mode()
m1.types("红色","金属")
#这是一个红色颜色金属材质的手机壳
"""


#class Student:
    
#类名的首地址需要大写 

'''
class 类名：
    属性/方法/pass
属性--变量
方法--函数
pass--占位符
我们在定义类的时候我们对于类中需要写什么我们还没有想好，
我们可以通过pass进行占位的操作的
'''
'''
class Pub:
    pass#进行占位
#这么就不会出现报错，但是我们如果不加pass的话，并且类中是空的，那么就会报错
'''
'''
class Student:
    pass
print(Student)
#<class '__main__.Student'>
s1=Student()
print(s1)
#<__main__.Student object at 0x0184F090>
'''
"""

class Student:
    #姓名、年龄、成绩---描述----属性
    def fun(self,name,age,grade):#接收传入的数据
        self.name=name
        self.age=age
        self.grade=grade
    #睡觉、吃饭、学习---动作、行为---方法  def 方法名()
    #self接受对象信息，即seif=对象
    def sleep(self):
        print(f'{self.name}正在睡觉')
        #在类里面进行实例属性的调用
    def eat(self):
        print(f'{self.name}正在吃东西')
        
    def study(self):
        print(f'{self.name}正在学习')
        
   

s1=Student()
s1.fun("小明",12,78)
#现在fun函数里面有四个参数，第一个self我们是不用传实参的
#self是可以自动传参的，不需要手动传参的
print(s1.age)

s1.sleep()
#小明正在睡觉

'''
实例属性一但被定义了，那么在实例方法中都能进行调用的
不存在之前函数内变量的局部域和全局域的说法
只要是实例方法的话就能进行实例属性的运用
'''
s1.eat()
#小明正在吃东西
s1.study()
#小明正在学习
'''
s2=Student()
s2.sleep()
这么写是会报错的，因为我们是没有进行对象内属性的初始化的
'''


#生成多个对象，每个对象的值都是不一样的


#我们在调用其他的实例方法的之前我们一定要对对象里面的属性进行初始化
#不然后面的方法是调用不了的

"""
"""
class Student:
    #姓名、年龄、成绩---描述----属性
    def fun(self,name,age,grade):#接收传入的数据
        self.name=name
        self.age=age
        self.grade=grade
    #睡觉、吃饭、学习---动作、行为---方法  def 方法名()
    #self接受对象信息，即seif=对象
    def sleep(self):
        print(f'{self.name}正在睡觉')
        #在类里面进行实例属性的调用
    def eat(self):
        print(f'{self.name}正在吃东西')
        
    def study(self):
        print(f'{self.name}正在学习')

class Test:
    def __init__(self,name,age):
        print("这是一个初始化方法")

t=Test()
#这是一个初始化方法
#我们在创建对象的时候这个init函数会自动执行的，自动进行初始化操作

#如果在__init__中有定义形参，那么创建对象时就要传递实参
#对于对象中都要使用到的通用属性的话，我们就会将这些属性放到__init__函数进行初始化操作
#伴随着对象的创建，那么这个对象的基本属性就初始化好了

#像是上面的fun函数我们必须先调用我们才能实现这个对象属性的初始化操作
#但是现在的话我们是不需要进行调用这个init函数
#我们对象一创建好这个init函数就自动进行调用了


#将一些必备的属性放在init函数中，创建对象就直接初始化好了，

"""
'''
class Student:
    #姓名、年龄、成绩---描述----属性
    def fun(self,name,age,grade):#接收传入的数据
        self.name=name
        self.age=age
        self.grade=grade
    #睡觉、吃饭、学习---动作、行为---方法  def 方法名()
    #self接受对象信息，即seif=对象
    def sleep(self):
        print(f'{self.name}正在睡觉')
        #在类里面进行实例属性的调用
    def eat(self):
        print(f'{self.name}正在吃东西')
        
    def study(self):
        print(f'{self.name}正在学习')
'''
"""
class Test:
    #构造函数
    def __init__(self,name):
        self.name=name#这个self就是指向的对象
        print("这是一个初始化方法")
    
    #析构函数--对对象进行销毁的操作
    def __del__(self):
        print(f'{self.name}进行销毁')

t=Test("张三")

print("程序执行完毕")
del t
'''
这是一个初始化方法
程序执行完毕
张三进行销毁
'''

#我们只有将这个t这个对象进行销毁了，才会进行这个析构函数
#如果这个t没有被回收的话是不会进行析构函数的

#我们这里是不会进行这个析构函数内的打印操作的，但是在paycharm中是会进行的
#手动的删除del执行时间是在程序结束之前进行的
"""
"""
class Test:
    #构造函数
    def __init__(self,name):
        self.name=name#这个self就是指向的对象
        print("这是一个初始化方法")
    
    #析构函数--对对象进行销毁的操作
    def __del__(self):
        print(f'{self.name}进行销毁')
        #设置print输出对象名时显示的内容
    def __str__(self):
        return "这是一个魔法方法__str__设置的"
    #__repr__(self):设置直接输出对象名时的显示内容
    def __repr__(self):
        return '这是__repr__设置显示的内容'

t1=Test("张三")
print(t1)
#这是一个魔法方法__str__设置的

'''
交互模式：
print(t1)
这是一个魔法方法__str__设置的
t1
这是__repr__设置显示的内容
'''


#isinstance(对象，类)
#判断对象是不是后面的类实例出的对象
print(isinstance(t1,Test))
#True

#就是判断我们第一个参数是不是通过第二个参数（类）实例化出来的
#这个就是进行类型的判断
print(isinstance(1,int))
#True
print(isinstance(1.5,int))
#False
"""
"""
class Father:#父亲类
    def money(self):
        print('100万')

#单继承：class子类名（父类名）
class Son(Father):#子类
    #重写：在子类中定义一个与父类方法同名的方法就是重写，那么这个子类就不会去执行父亲类了
    def money(self):
        print('1000万')

#用儿子类创建出来的对象进行父亲类中实例方法的调用
s=Son()
s.money()
#100万    
        

#子类可以通过继承父类，获取父类中的方法、属性

#__mro__:查询搜索顺序
print(Son.__mro__)
"""
#(<class '__main__.Son'>, <class '__main__.Father'>, <class 'object'>)
#我们在执行这个money的时候我们先看自己类有没有这个方法
#如果自己类有这个方法的话就调用自己的，如果没有的话就看看父亲类有没有
#如果父亲类没有的话就去基类里面找，但是基类的话可能没有，那么就会进行报错的操作

#自己有就用自己的，自己没有就用父亲的

#但是我们进行重写操作之后又想用父亲类里面的，该怎么做呢？
#我们可以进行扩展：
#扩展可以执行父类中的同名方法
#方法一：父类名().方法名(self)
'''

class Father:
    def money(self):
        print('100万')


class Son(Father):
    def money(self):
        print('1000万')
#扩展可以执行父类中的同名方法
#方法一：父类名.方法名(self)
        Father.money(self)
#方法二：super().方法名()#调用下一个类中的方法
        #就是我们的搜索顺序是子类到父类到基类
        #那么调用下一个类中的方法就是去父类里面找
        super().money()
s=Son()
s.money()
#1000万
#100万
#通过方法一：我们也能调用父亲类里面的money方法了
#100万--第三种方法

'''
'''
#一个子类同时继承多个父类就是多继承
class Father:
    def money(self):
        print('100')
class Mother:
    
    def host(self):
        print("三室一厅")
class Son(Father,Mother):
    pass

s=Son()
s.money()
s.host()
#100
#三室一厅

'''
class Father:
    def money(self):
        print('100')
class Mother:
    def money(self):
        print('1000')
    def host(self):
        print("三室一厅")
class Son(Father,Mother):
    #重写操作
    def money(self):
        print("10")
        #扩展
        #扩展一：super().方法名()---这个扩展的是下一个类里面的对应的方法，就是Father里面的方法
        super().money()#子类后面的类中第一个有money的类

        #扩展二   父类名.方法()
        Father.money(self)
        Mother.money(self)
s=Son()
s.money()
s.host()

#现在父亲类和母亲类都有money
#那么儿子调用money的时候是调用谁的呢？

#100
#三室一厅
#为什么是这样的呢？
#我们可以试试看看搜索顺序是如何的

print(Son.__mro__)
#(<class '__main__.Son'>, <class '__main__.Father'>, <class '__main__.Mother'>, <class 'object'>)

#先是子类，再是父类，再是母类，最后是基类
#这个顺序依照的就是我们在子类继承多类的时候写在括号内的顺序的
#class Son(Father,Mother):
#如果我们先写的是Mother的话，那么这个搜索顺序肯定是Mother排第二个

















